package com.zjmiec.utils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.sl.usermodel.PictureShape;
import org.apache.poi.sl.usermodel.TextBox;
import org.apache.poi.xslf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Data
@Slf4j
public class powerPointUtil {
    /**
     * PPT文件
     */
    private XMLSlideShow pptx;

    public powerPointUtil(XMLSlideShow pptx) {
        this.pptx = pptx;
    }

    public static void mergePPT(ArrayList<byte[]> list, String rootDir, Map<String, String> paramMap) throws IOException {
        XMLSlideShow ppt = new XMLSlideShow(new FileInputStream(rootDir));
            //图片替换
        if (list.size()>=4){
            powerPointUtil.feedImageData(ppt, list.get(0), 1);
            powerPointUtil.feedImageData(ppt, list.get(0), 2);
            powerPointUtil.feedImageData(ppt, list.get(1), 3);
            powerPointUtil.feedImageData(ppt, list.get(2), 4);
            powerPointUtil.feedImageData(ppt, list.get(3), 5);
        }else if (list.size()==3){
            powerPointUtil.feedImageData(ppt, list.get(0), 1);
            powerPointUtil.feedImageData(ppt, list.get(0), 2);
            powerPointUtil.feedImageData(ppt, list.get(1), 3);
            powerPointUtil.feedImageData(ppt, list.get(2), 4);
        }else if (list.size()==2){
            powerPointUtil.feedImageData(ppt, list.get(0), 1);
            powerPointUtil.feedImageData(ppt, list.get(0), 2);
            powerPointUtil.feedImageData(ppt, list.get(1), 3);
        }else if(list.size()==1) {
            powerPointUtil.feedImageData(ppt, list.get(0), 1);
            powerPointUtil.feedImageData(ppt, list.get(0), 2);
        }
        // PPT通用处理
        powerPointUtil.pptCommentHandle(ppt, paramMap);
        // 3. 输出
        String resultName = "Result.pptx";
        FileOutputStream out = new FileOutputStream(resultName);
        ppt.write(out);
        out.close();
    }
    /**
     * PPT通用处理（文本和表格）
     * @param pptx     PPT
     * @param paramMap 参数map
     * @return void
     */
    public static void pptCommentHandle(XMLSlideShow pptx, Map<String, String> paramMap) {
        powerPointUtil powerPointUtil = new powerPointUtil(pptx);
        // 遍历幻灯片
        List<XSLFSlide> slideList = pptx.getSlides();
        for (XSLFSlide slide : slideList) {
            // 1. 替换段落占位符
            // 1.1 获取所有的shape，并解析为文本段落
            List<XSLFShape> shapes = slide.getShapes();
            List<XSLFTextParagraph> paragraphsFromSlide = new ArrayList<>();
            for (XSLFShape shape : shapes) {
                List<XSLFTextParagraph> textParagraphs = powerPointUtil.parseParagraph(shape);
                paragraphsFromSlide.addAll(textParagraphs);
            }
            // 1.2 替换文本段落中的占位符
            for (XSLFTextParagraph paragraph : paragraphsFromSlide) {
                powerPointUtil.replaceTagInParagraph(paragraph, paramMap, -1);
            }
            // 2. 替换表格内占位符
            // 2.1 循环获取到表格的单元格，并获取到文本段落
            List<XSLFTable> allTableFromSlide = powerPointUtil.getAllTableFromSlide(slide);
            for (XSLFTable xslfTableRows : allTableFromSlide) {
                List<XSLFTableRow> rows = xslfTableRows.getRows();
                for (XSLFTableRow row : rows) {
                    for (XSLFTableCell cell : row.getCells()) {
                        List<XSLFTextParagraph> textParagraphs = cell.getTextParagraphs();
                        for (XSLFTextParagraph textParagraph : textParagraphs) {
                            // 2.2 替换文本段落中的占位符
                            powerPointUtil.replaceTagInParagraph(textParagraph, paramMap, -1);
                        }
                    }
                }
            }
        }
    }
    /**
     * 从幻灯片中获取表格列表
     * @param slide 幻灯片
     * @return 表格列表
     */
    public List<XSLFTable> getAllTableFromSlide(XSLFSlide slide) {
        List<XSLFTable> tables = new ArrayList<>();
        for (XSLFShape shape : slide.getShapes()) {
            if (shape instanceof XSLFTable) {
                tables.add((XSLFTable) shape);
            }
        }
        return tables;
    }

    /**
     * 替换段落内的标签文本
     *
     * @param paragraph 段落
     * @param paramMap  参数Map
     * @param start     替换位置索引
     * @return void
     */
    public void replaceTagInParagraph(XSLFTextParagraph paragraph, Map<String, String> paramMap, int start) {
        String paraText = paragraph.getText();
        // 正则匹配，循环匹配替换
        String regEx = "\\$\\{.+?\\}";
        Pattern pattern = Pattern.compile(regEx);
        Matcher matcher = pattern.matcher(paraText);
        while (matcher.find()) {
            StringBuilder keyWord = new StringBuilder();
            // 获取占位符起始位置所在run的索引
            int s = getRunIndex(paragraph, "${", start);
            if (s < start) {
                // 重复递归，直接返回
                return;
            }
            // 获取占位符结束位置所在run的索引
            int e = getRunIndex(paragraph, "}", start);
            // 存放标签
            String rs = matcher.group(0);
            // 存放 key
            keyWord.append(rs);
            // 获取标签所在 run 的全部文字
            String text = getRunsT(paragraph, s, e + 1);
            // 如果没在 paramMap，则不做替换
            String v = nullToDefault(paramMap.get(keyWord.toString()), keyWord.toString());
            // 没有找到这个标签所对应的值，那么就直接替换成标签的值（业务需求来着，找不到不替换）
            setText(paragraph.getTextRuns().get(s), text.replace(rs, v));
            // 存在 ${ 和 } 不在同一个CTRegularTextRun内的情况，将其他替换为空字符
            for (int i = s + 1; i < e + 1; i++) {
                setText(paragraph.getTextRuns().get(i), "");
            }
            start = e + 1;
        }
    }

    /**
     * 解析一个shape内的所有段落
     *
     * @param shape shape
     * @return 文本段落列表
     */
    public List<XSLFTextParagraph> parseParagraph(XSLFShape shape) {
        if (shape instanceof XSLFAutoShape) {
            XSLFAutoShape autoShape = (XSLFAutoShape) shape;
            return autoShape.getTextParagraphs();
        } else if (shape instanceof XSLFTextShape) {
            XSLFTextShape textShape = (XSLFTextShape) shape;
            return textShape.getTextParagraphs();
        } else if (shape instanceof XSLFFreeformShape) {
            XSLFFreeformShape freeformShape = (XSLFFreeformShape) shape;
            return freeformShape.getTextParagraphs();
        } else if (shape instanceof TextBox) {
            TextBox textBox = (TextBox) shape;
            return textBox.getTextParagraphs();
        }
        return new ArrayList<>();
    }

    /**
     * 获取段落下特定索引的textRun的值
     * @param paragraph 段落
     * @param start     起始位置
     * @param end       终止位置
     * @return run值
     */
    private String getRunsT(XSLFTextParagraph paragraph, int start, int end) {
        List<XSLFTextRun> textRuns = paragraph.getTextRuns();
        StringBuilder t = new StringBuilder();
        for (int i = start; i < end; i++) {
            t.append(textRuns.get(i).getRawText());
        }
        return t.toString();
    }

    /**
     * 设置run的值
     *
     * @param run run
     * @param t   run值
     * @return void
     */
    private void setText(XSLFTextRun run, String t) {
        run.setText(t);
    }

    /**
     * 获取word在段落中出现第一次的run的索引
     * @param paragraph 段落
     * @param word      目标值
     * @param start     索引
     * @return void
     */
    private int getRunIndex(XSLFTextParagraph paragraph, String word, int start) {
        List<CTRegularTextRun> rList = paragraph.getXmlObject().getRList();
        for (int i = (Math.max(start, 0)); i < rList.size(); i++) {
            String text = rList.get(i).getT();
            if (text.contains(word)) {
                return i;
            }
        }
        return -1;
    }
    /**
     * 替换图片信息
     * @param ppt
     * @param pictureData 图片数据
     * @param index       索引
     */
    public static void feedImageData(XMLSlideShow ppt, byte[] pictureData, int index) throws IOException {
        int num = 0;
        for (XSLFSlide slide : ppt.getSlides()) {
            // 获取每一张幻灯片中的shape
            for (XSLFShape shape : slide.getShapes()) {
                if (shape instanceof PictureShape) {
                    if (num == index) {
                        XSLFPictureShape pictureShape = (XSLFPictureShape) shape;
                        pictureShape.getPictureData().setData(pictureData);
                        break;
                    }
                    num++;
                }
            }
        }
    }

    /**
     * toString方法，空则返回默认值
     * @param o          对象
     * @param defaultStr 默认值
     * @return toString
     */
    private String nullToDefault(Object o, String defaultStr) {
        if (o.equals(null)) {
            return defaultStr;
        }
        return o.toString();
    }
}